# Python 类 (Class)

# 类的成员
# 实例变量：每个实例对象都有自己的数据，这些数据称为实例变量。实例变量可以被直接访问、修改。
# 类变量：类变量是所有实例共享的数据，它是类所有实例所共有的数据，可以被所有实例访问、修改。
# 构造方法：在创建对象时，系统自动调用构造方法，为对象初始化数据。
# 实例方法：类的方法，用于操作实例变量。
# 类方法：与实例方法类似，但它们的第一个参数是类本身，而不是实例对象。
# 静态方法：与实例方法和类方法类似，但不需要访问实例或类变量。


# 类方法和静态方法的区别：
# 类方法：第一个参数必须是 cls，表示当前类对象，可以访问类变量和静态方法。
# 静态方法：不需要访问实例或类变量，只能访问全局变量和局部变量。
# 一般情况下，类方法和静态方法的区别并不重要，因为它们都可以访问实例变量和类变量。


# 类变量与实例变量的区别：
# 类变量：所有实例共享，可以被所有实例访问、修改。
# 实例变量：每个实例对象都有自己的数据，可以被直接访问、修改。
# 类变量通常用于存储类相关的数据，如类名、类方法等；实例变量通常用于存储实例相关的数据，如实例名、实例变量等。


# 构造函数：__init__
# 构造函数是类的特殊方法，当创建对象时，系统自动调用构造函数，为对象初始化数据。
# 构造函数的第一个参数必须是 self，表示创建的对象本身。
# 构造函数可以有参数，参数可以用于初始化实例变量。
# 构造函数的返回值没有意义，可以省略。

# 构造函数的调用时机：
# 1. 创建对象时，系统自动调用构造函数。
# 2. 调用对象的方法时，如果该方法需要访问实例变量，系统会自动调用构造函数。
# 3. 调用类方法时，系统会自动调用构造函数。
# 4. 调用静态方法时，系统不会自动调用构造函数。


# 实例方法：
# 实例方法是类的成员函数，用于操作实例变量。
# 实例方法的第一个参数必须是 self，表示当前实例对象。
# 实例方法可以访问实例变量，也可以修改实例变量。
# 实例方法只能被实例对象调用，不能被类对象调用。


# self 相当于Java中的 this，表示当前实例对象。
# self 表示当前实例对象本身，可以访问实例变量和方法。

# 定义一个全局变量
role = "Python大神"

# 定义类
class Person:
    # 类变量--所有实例共享(相当于Java中的静态变量)
    species = "千江镇"
    total = 0

    # 构造方法--初始化实例变量
    # 第一个参数必须是 self，表示创建的对象本身
    def __init__(self, name, age):
        # 实例变量
        self.name = name
        self.age = age
        # 定义一个示例变量与类变量同名(通常不建议这么干)
        #self.species = species

        # 通过当前实例对象(self)的 __class__(类本身) 属性访问类变量
        self.__class__.total += 1  # 类变量total自增(记录创建的对象数量)

    # 实例方法
    # 第一个参数必须是 self，表示当前实例对象
    # 与具体实例相关，用于操作实例变量
    def say_hello(self):
        # 访问实例变量
        print("fuck you " + self.name)

    def get_name(self):
        # 访问实例变量时必须使用self来访问，否则会报错
        #return name
        return self.name


    # 类方法
    # 与类相关，用于操作类变量，虽然实例对象也可以访问类变量，但不建议这么干
    # 第一个参数是类本身，通过类调用可以访问类变量和静态方法
    @classmethod
    def get_species(cls):
        # 访问类变量
        return cls.species

    # 静态方法
    # 静态访问跟类本身和实例对象没有关联，仅仅只是定义在类里面的一个普通方法
    # 静态方法内部不能访问实例变量和类变量，只能访问全局变量和局部变量
    @staticmethod
    def print_species():
        # 访问全局变量
        print(f"你特么是 {role}？")
        # 静态方法内部访问类变量必须通过类名来访问
        print("这是一个 " + Person.species)

# 创建实例对象
person1 = Person("绿萝", 25)
person2 = Person("鬼藤", 30)

# 调用实例方法
person1.say_hello()
person2.say_hello()

# 通过实例访问实例变量--每个实例对象都有自己的数据，可以看到每个实例都有不同的name值
print("p1的name是", person1.name)
print("p2的name是", person2.name)

# 通过实例访问类变量--所有实例共享，可以看到所有实例都有相同的species值
# 但是，不建议这么干，因为通过实例对象访问类变量时，会首先从实例对象查找，如果找不到，再从类对象查找，这会导致逻辑混乱
print("p1的species是", person1.species)
print("p2的species是", person2.species)

# 调用类方法
print(Person.get_species())

# 调用静态方法
Person.print_species()


# __dict__
# 实例对象有一个__dict__属性，它是一个字典，包含了实例变量和方法。
# 可以通过__dict__属性查看实例变量和方法。
print(person1.__dict__)
print(person2.__dict__)

# 类对象有一个__dict__属性，它是一个字典，包含了类变量和方法。
# 可以通过__dict__属性查看类变量和方法。
print(Person.__dict__)

print(f"总共创建了{Person.total}个对象")